#ifndef SESSION_H
#define SESSION_H

#include <memory>

#include "Poco/Net/StreamSocket.h"
#include "Poco/Net/SocketReactor.h"
#include "Poco/RefCountedObject.h"
#include "Poco/Net/SocketAcceptor.h"
#include "Poco/Mutex.h"
#include "readerwriterqueue/readerwriterqueue.h"

#include "./Common.h"

namespace Reactor
{
	class MOUDLE_API Session;
	struct MOUDLE_API DataPacket;
	class MOUDLE_API SubReactor;

    class MOUDLE_API Reader
    {
    public:
        Reader(std::shared_ptr<Session> _session);
        int peek(char* _src, std::streamsize n);
        int peek(Poco::Buffer<char>& _src, std::streamsize n);
        int read(char* _src, std::streamsize n);
        int read(Poco::Buffer<char>& _src, std::streamsize n);
        char ch();
    protected:
        std::shared_ptr<Session> _session;
    };

    /**
     *  Session唯一标识Id
     */

	using SessionId			= std::string;
    /**
     *  Session内嵌是智能指针
     */

	using SessionAutoPtr	= std::shared_ptr<Session>;
    /**
     *  数据包智能指针
     */
	using DataPacketPtr		= std::shared_ptr<DataPacket>;

    /**
     * @brief The DataPacket class
     */
	struct MOUDLE_API DataPacket
	{
		SessionAutoPtr		_sessionPtr;
		Poco::Buffer<char>	_headBuffer;
		Poco::Buffer<char>  _bodyBuffer;

		DataPacket(int _headSize, int _bodySize);
		int packetSize()const;
	};

	struct MOUDLE_API SessionPrivate;

    /**
     * @brief The Session class
     */
	class  MOUDLE_API Session : public std::enable_shared_from_this<Session>
	{
        friend class Reader;
    public:
        enum class ProtocolCurrentParseStatus
        {
            PORTOCOL_PARSE_DONE,
            PORTOCOL_PARSE_ERROR,
            PORTOCOL_PARSE_HEAD_ING,
            PORTOCOL_PARSE_BODY_START,
            PORTOCOL_PARSE_BODY_ING
        };

        enum class ProtocolParseState
        {
            PORTOCOL_PARSE_HEAD_CORRECT,
            PORTOCOL_PARSE_HEAD_PART_CORRECT,
            PORTOCOL_PARSE_HEAD_ERROR,
            PORTOCOL_PARSE_BODY_CORRECT,
            PORTOCOL_PARSE_BODY_PART_CORRECT,
            PORTOCOL_PARSE_BODY_ERROR
        };

	public:
		Session(Poco::Net::StreamSocket _socket, const SessionOp& _sessionop);
		Session(Poco::Net::StreamSocket _socket, Poco::Net::SocketReactor& _reactor, const SessionOp& _sessionop);
		~Session();
		
	public:
        /**
         * @brief enqueue: 将打包好的数据包存入Session缓冲队列，内部带有重试机制
         * @param _packet: 需要存入的数据包
         * @return: 是否成功
         */
		bool enqueue(const std::shared_ptr<DataPacket>& _packet);

        /**
         * @brief socket：获取内部socket引用
         * @return：内部socket引用
         */
		Poco::Net::StreamSocket& socket();

        /**
         * @brief peerAddress：获取客户端IP地址 + PORT
         * @return: 客户端IP地址 + PORT
         */
		Poco::Net::SocketAddress peerAddress()const;

        /**
         * @brief serverAddress: 获取服务器IP地址 + PORT
         * @return: 服务器IP地址 + PORT
         */
		Poco::Net::SocketAddress serverAddress()const;

        /**
         * @brief reactor: 获取Session内部绑定的Poco::Net的原生Reactor, 不可手动释放
         * @return: Session内部绑定的Poco::Net的原生Reactor
         */
		Poco::Net::SocketReactor* reactor()const;

        /**
         * @brief subReactor：获取带有数据缓存队列的Reactor, 继承自Poco::Net::SocketReactor
         * @return：带有数据缓存队列的Reactor
         */
		SubReactor* subReactor()const;

        /**
         * @brief self: 获取Session的内嵌式智能指针
         * @return：Session的内嵌式智能指针
         */
		std::shared_ptr<Session> self();

        /**
         * @brief sessionId: 获取Session的唯一Id
         * @return: Session的唯一Id
         */
		SessionId sessionId()const;

        /**
         * @brief isBlocking: 获取内部的socket是否是非阻塞的
         * @return: 是否是非阻塞的
         */
		bool isBlocking()const;
	
        bool isValid()const;

        void setReactor(Poco::Net::SocketReactor& reactor);
        Poco::UInt64 lastAlive()const;
        static Poco::UInt64 current();
        void prepareStop(const void* _pSender);

    public:
        virtual ProtocolParseState parseHead(Reader& headreader) = 0;
        virtual ProtocolParseState parseHeadContinue(Reader& headreader) = 0;
        virtual ProtocolParseState parseBody(Reader& bodyreader) = 0;
        virtual ProtocolParseState parseBodyContinue(Reader& bodyreader) = 0;
		virtual DataPacketPtr currentPacket() = 0;
        virtual DataPacketPtr postError() = 0;
		virtual void parseError() = 0;

    protected:
        void updateAlive();
        bool checkHeadValid(const Poco::Buffer<char>& _headSource);
        void registerNotify(Poco::Net::SocketReactor* _reactor);
        void unregisterNotify();
        bool valid();
        bool isPrepareStop();
        void startStop();
        void refill();
        /**
         * @brief enableRead: 添加可读事件
         */
		void enableRead();

        /**
         * @brief disableRead: 禁止可读事件
         */
		void disableRead();

        /**
         * @brief hasRead: 是否已经添加可读事件
         * @return
         */
		bool hasRead();

        /**
         * @brief enableWrite: 添加可写事件
         */
		void enableWrite();

        /**
         * @brief disableWrite: 禁止可写事件
         */
		void disableWrite();

        /**
         * @brief hasWrite: 是否已经添加可写事件
         * @return
         */
		bool hasWrite();

        /**
         * @brief enableError: 添加错误事件
         */
		void enableError();

        /**
         * @brief disableError: 禁止错误事件
         */
		void disableError();

        /**
         * @brief hasError: 是否已经添加错误事件
         * @return
         */
		bool hasError();

        /**
         * @brief enableAll: 启用全部事件
         */
		void enableAll();

        /**
         * @brief disableAll: 禁用全部事件
         */
		void disableAll();

        /**
         * @brief writePacket: 将_packet数据包写入Session内部发送缓冲队列
         * @param _packet: 需要写入的数据包
         * @return: 是否写入成功
         */
		bool writePacket(const DataPacketPtr& _packet);

        /**
         * @brief onRead: 可读事件到来的回调函数
         * @param pNf: 通知器
         */
		void onRead(Poco::Net::ReadableNotification* pNf);

        /**
         * @brief onWrite: 可写事件到来的回调函数
         * @param pNf: 通知器
         */
		void onWrite(Poco::Net::WritableNotification* pNf);

        /**
         * @brief onTimeout: 超时事件到来的回调函数
         * @param pNf: 通知器
         */
		void onTimeout(Poco::Net::TimeoutNotification* pNf);

        /**
         * @brief onError: 错误事件到来的回调函数
         * @param pNf: 通知器
         */
		void onError(Poco::Net::ErrorNotification* pNf);

        /**
         * @brief available: 是否有数据可读
         * @return: true: 有， false: 没有
         */
		int available()const;

        /**
         * @brief bufferCanWriteSize：内部缓存待发送数据的缓冲区可写入大小
         * @return：可写入的字节数大小
         */
		int bufferCanWriteSize()const;

        /**
         * @brief close：关闭socket
         */
		void close();

        /**
         * @brief setBlocking: 设置socket是否阻塞
         * @param _blocking
         */
		void setBlocking(bool _blocking);

        /**
         * @brief packetQueue：获取内部数据包缓冲队列引用
         * @return
         */
		moodycamel::ReaderWriterQueue<DataPacketPtr>& packetQueue();

        /**
         * @brief sendFifoBuffer: 获取内部发送缓冲区
         * @return
         */
		Poco::FIFOBuffer& sendFifoBuffer();

        /**
         * @brief recvFifoBuffer: 获取内部接受缓冲区
         * @return
         */
		Poco::FIFOBuffer& recvFifoBuffer();

        /**
         * @brief parsePackets: 对当前数据接受缓冲区的数据进行解析数据包，可以一次解析多个，并且投递到SubReactor的数据包队列
         * @return
         */
		void protocolParse(Reader& reader);
		bool parsePackets();
        void setCurrent(ProtocolCurrentParseStatus status);
        ProtocolCurrentParseStatus currentStatus();
        bool needQuit();
        /**
         * @brief postPacket：投递接受到的_packet到SubReactor的数据包队列
         * @param _packet: 接受到的数据包
         * @return
         */
		bool postPacket(const DataPacketPtr& _packet);

	protected:
		std::shared_ptr<SessionPrivate> d;
	};
}
#endif // !SESSION_H
